
import datetime
import threading
import time
import logging

from datastore.resource import *
from datastore.uri import *
from runtime.task import *

class TimeTask(Task):
    """
    Task to assist with timer and cron activities. 
    
    A TimeTask client will have to 'put' to either the 'timer' or 'cron' uri to 
    create a timer or cron job. The resource will contain the interval at which the
    client will want to be notified. The job will run as a separate thread and 'put' to the time job uri to 
    notify the client, so the client will need to watch/process this uri. Once the client no longer requires
    the time job, the client can delete it by deleting the resource at the time job uri.
    """
    
    def cb_start(self, method, uri, res, result, spaces_context, **kwargs):
        task_def = spaces_context.get(res)
        self.logger.log(logging.INFO, "Timestamp service uri %s" % task_def.options.timestamp)
        spaces_context.process(
            task_def.options.timestamp, get='cb_timestamp')
        spaces_context.watch_all(['put'], 
            task_def.options.timer, 'cb_create_timer')
        spaces_context.watch_all(['delete'], 
            task_def.options.timer, 'cb_delete_time_job')
        spaces_context.watch_all(['put'],
            task_def.options.cron, 'cb_create_cron')
        spaces_context.watch_all(['delete'], 
            task_def.options.cron, 'cb_delete_time_job')
        
        # a dictionary of all the current running time jobs.
        self.jobs = {}
    
    def cb_timestamp(self, method, uri, res, result, spaces_context, **kwargs):
        '''
        Returns the a timestamp of the current time back to the user.
        '''
        try:
            self.logger.log(logging.INFO, "Timestamp service called.")
            date_time_now = datetime.datetime.now()
            rs = Resource()
            rs._logger = logging.getLogger('yarra.runtime.task.Task.time_task.resource')
            rs.timestamp = date_time_now
            print("Timestamp service result %s." % rs)
            return (True, rs)
        except:
            exc_info = sys.exc_info()
            self.logger.exception("Timestamp service error. Exception: %s. Value: %s." % (exc_info[0], exc_info[1]))
        return (True, None)
    
    def cb_create_timer(self, method, uri, new_uri, result, spaces_context, **kwargs):
        '''
        Creates a TimeJob that will put to the timer uri at the interval specified. Users will have to 
        watch/process that uri for the 'put' and then delete the timer.
        '''
        uri_time = spaces_context.uri('time:')
        if uri != uri_time.timer.collection(True):
            return
        res = spaces_context.get(new_uri)        
        interval = res.interval
        time_job = self.create_time_job(interval, spaces_context, new_uri, False)
        time_job.start()
    
    def cb_create_cron(self, method, uri, new_uri, result, spaces_context, **kwargs):
        '''
        Creates a TimeJob that will put to the timer uri at the interval specified. Users will have to 
        watch/process that uri for the 'put's and then delete the timer.
        '''
        uri_time = spaces_context.uri('time:')
        if uri != uri_time.cron.collection(True):
            return
        res = spaces_context.get(new_uri)
        interval = res.interval
        time_job = self.create_time_job(interval, spaces_context, new_uri, True)
        time_job.start()
    
    def create_time_job(self, interval, spaces_context, time_job_uri, is_cron):
        '''
        Creates a TimeJob with the specified interval and adds it to the jobs dictionary.
        '''
        time_job = TimeJob(interval, spaces_context, is_cron, time_job_uri)
        self.jobs[time_job_uri] = time_job 
        return time_job
    
    def cb_delete_time_job(self, method, uri, res, result, spaces_context, **kwargs):
        '''
        Stops and deletes the time job from the job dictionary.
        '''
        time_job = self.jobs[uri]
        time_job.stop_job()
        del self.jobs[uri]
    
class TimeJob(threading.Thread):
    '''
    TimeJob is a thread which represents either a timer or cron job. At the interval specified, it 'put's to 
    the time_job_uri. If it is a timer job, it will 'put' to the time_job_uri.pop only once then the thread will finish,
    otherwise if it is a cron job, it will sleep for the specified interval and then 'put' to the time_job_uri.pop again.
    The cron job must be stopped by the TimeTask.
    '''
    def __init__(self, interval, spaces_context, cron, time_job_uri):
        threading.Thread.__init__(self)
        self.name = self.__class__.__name__
        self.interval = interval
        self.spaces_context = spaces_context
        # indicates if it is a cron or timer job.
        self.cron = cron
        self.stop = False
        self.loop_counter = 0
        # uri which the TimeJob will 'put' to
        self.time_job_uri = time_job_uri
        
    def run(self):
        # sleep for the interval then wake up and start the loop
        time.sleep(self.interval)       
        # loop if its not stop and if it is either a cron job or a timer task and the run count is Zero.
        while ((self.loop_counter == 0 and not self.cron) or self.cron) and not self.stop:
            res = Resource()        
            self.spaces_context.put(self.time_job_uri.pop, res)
            # increment the loop_counter
            self.loop_counter += 1
            # sleep for the interval then wake up and put to the time_job_uri
            time.sleep(self.interval)
            
    def stop_job(self):
        '''
        Stop job by setting the stop flag to break the loop on next iteration.
        '''
        self.stop = True